#include "coating_pro.h"
#include "stdbool.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "jsmn_parser.h"
#include "puresun_func.h"

// internal function
coa_err_t coating_programs_load(coating_program_t *cp, int cp_id, js_vessel_t *js_vessel);
int get_coating_program_count(coating_program_list_t *cpl);
void free_program_mem(coating_program_t **cp);
void free_step_mem(step_t **step);
void free_steps_mem(step_t **steps);
step_t *new_step(int id, bool status, string notes, int duration, int cooldown, int power);
coa_err_t rank_program(coating_program_list_t *cpl);
coa_err_t rank_step(coating_program_list_t *cpl, int pid);

/*******************************************************************************
 - Function Name  : copy_program
 - Description    : copy the coating program and insert to the coating program
 -                  list after the program id
 - Input          : [coating_program_list_t *cpl, int pid]
 -                 - cpl: the coating program list pointer
 -                 - pid: the coating program id to be copied
 - Output         : None
 - Return         : coa_err_t
 -                 - COA_OK: success
 -                 - COA_ERR: failed
 -                 - COA_ERR_CPL_IS_NULL: the cpl is null
 -                 - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                 - COA_ERR_NO_MEM: no memory
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t copy_program(coating_program_list_t *cpl, int pid)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        if (cp->id == pid)
        {
            // copy the coating program
            coating_program_t *new_cp = malloc(sizeof(coating_program_t));
            if (new_cp == NULL)
            {
                return COA_ERR_NO_MEM;
            }

            // copy the coating program value
            memcpy(new_cp, cp, sizeof(coating_program_t));

            // insert the coating program
            cp->next = new_cp;

            // rank the coating program
            return rank_program(cpl);
        }
        cp = cp->next;
    }

    // can not find target coating program
    if (cp == NULL)
    {
        return COA_ERR_PID_NOT_FOUND;
    }

    return COA_OK;
}

/*******************************************************************************
 - Function Name  : copy_step
 - Description    : copy the coating program step and insert to the coating program
 -                  step list after the step id
 - Input          : [coating_program_list_t *cpl, int pid, int step_id]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 -                  - step_id: the coating program step id to be copied
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                  - COA_ERR_STEP_ID_NOT_FOUND: the step id is not found
 -                  - COA_ERR_NO_MEM: no memory
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t copy_step(coating_program_list_t *cpl, int pid, int step_id)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        if (cp->id == pid)
        {
            // find the step
            step_t *step = cp->steps;
            // step_t *step_prev = NULL;
            while (step)
            {
                if (step->id == step_id)
                {
                    // copy the step
                    step_t *new_step = malloc(sizeof(step_t));
                    string notes = malloc(strlen(step->notes) + 1);
                    if (new_step == NULL || notes == NULL)
                    {
                        if (new_step != NULL)
                        {
                            free(new_step);
                        }
                        if (notes != NULL)
                        {
                            free(notes);
                        }
                        return COA_ERR_NO_MEM;
                    }

                    // copy the step value
                    memcpy(new_step, step, sizeof(step_t));
                    strcpy(notes, step->notes);
                    step->notes = notes;

                    // insert the step
                    step->next = new_step;

                    // cp->steps_count = get_steps_count(cpl, pid);

                    // rank the step
                    return rank_step(cpl, pid);
                }
                step = step->next;
            }
        }
        cp = cp->next;
    }

    // can not find the coating program
    if (cp == NULL)
    {
        return COA_ERR_PID_NOT_FOUND;
    }

    return COA_OK;
}

/*******************************************************************************
 - Function Name  : move_step
 - Description    : move the coating program step
 - Input          : [coating_program_list_t *cpl, int pid, int step_id, bool forward]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 -                  - step_id: the coating program step id
 -                  - up: true: move up, false: move down
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                  - COA_ERR_STEP_ID_NOT_FOUND: the step id is not found
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t move_step(coating_program_list_t *cpl, int pid, int step_id, bool forward)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *currp = cpl->program;

    while (currp)
    {
        if (currp->id == pid)
        {
            // find the step
            step_t **head = &currp->steps;
            step_t *curr = *head;
            step_t *prev = NULL;
            step_t *pprev = NULL;
            while (curr)
            {
                if (curr->id == step_id)
                {
                    if (forward)
                    {
                        // the step is the first step, do nothing
                        if (prev == NULL)
                        {
                            return COA_OK;
                        }

                        // the step is the second step
                        prev->next = curr->next;
                        curr->next = prev;
                        if (pprev == NULL)
                        {
                            *head = curr;
                        }
                        else
                        {
                            pprev->next = curr;
                        }
                    }
                    else
                    {
                        // the step is the last step, do nothing
                        if (curr->next == NULL)
                        {
                            return COA_OK;
                        }

                        // the step is the head step
                        if (prev == NULL)
                        {
                            *head = curr->next;
                            curr->next = curr->next->next;
                            (*head)->next = curr;
                        }
                        else
                        {
                            prev->next = curr->next;
                            curr->next = curr->next->next;
                            prev->next->next = curr;
                        }
                    }

                    return rank_step(cpl, pid);
                }
                pprev = prev;
                prev = curr;
                curr = curr->next;
            }
            break;
        }
    }
    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : rank_step
 - Description    : rank the coating program step
 - Input          : [coating_program_list_t *cpl, int pid]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t rank_step(coating_program_list_t *cpl, int pid)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        if (cp->id == pid)
        {
            // rank the step
            step_t *step = cp->steps;
            int i = 1;
            while (step)
            {
                step->id = i;
                step = step->next;
                i++;
            }
            cp->steps_count = i - 1;
            return COA_OK;
        }
        cp = cp->next;
    }

    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : rank_program
 - Description    : rank the coating program
 - Input          : [coating_program_list_t *cpl]
 -                  - cpl: the coating program list pointer
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t rank_program(coating_program_list_t *cpl)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }
    cpl->program_count = get_coating_program_count(cpl);
    coating_program_t *cp = cpl->program;
    int i = 1;
    while (cp)
    {
        cp->id = i;
        cp = cp->next;
        i++;
    }
    return COA_OK;
}

/*******************************************************************************
 - Function Name  : delete_step
 - Description    : delete the coating program step
 - Input          : [coating_program_list_t *cpl, int pid, int step_id]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 -                  - step_id: the coating program step id
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                  - COA_ERR_STEP_ID_NOT_FOUND: the step id is not found
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t delete_step(coating_program_list_t *cpl, int pid, int step_id)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = get_program_by_id(cpl, pid);
    if (cp == NULL)
    {
        return COA_ERR_PID_NOT_FOUND;
    }

    // find the step
    step_t *step = cp->steps;
    step_t *step_prev = NULL;
    while (step)
    {
        if (step->id == step_id)
        {
            if (cp->current_step == NULL)
            {
                // do nothing
            }
            else if (cp->current_step == step)
            {
                cp->current_step = NULL;
            }
            else if (cp->current_step->id > step_id)
            {
                cp->current_step->id--;
            }
            else
            {
                // do nothing
            }

            // delete the step
            if (step_prev == NULL)
            {
                cp->steps = step->next;
            }
            else
            {
                step_prev->next = step->next;
            }
            free_step_mem(&step);
            cp->steps_count = get_steps_count(cpl, pid);

            // rank the step
            return rank_step(cpl, pid);
        }
        step_prev = step;
        step = step->next;
    }
    return COA_ERR_STEP_ID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : delete_program
 - Description    : delete the coating program
 - Input          : [coating_program_list_t *cpl, int pid]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t delete_program(coating_program_list_t *cpl, int pid)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = cpl->program;
    coating_program_t *cp_prev = NULL;
    while (cp)
    {
        if (cp->id == pid)
        {
            // delete the coating program
            if (cp_prev == NULL)
            {
                cpl->program = cp->next;
            }
            else
            {
                cp_prev->next = cp->next;
            }
            free_program_mem(&cp);

            // rank the coating program
            coa_err_t err = rank_program(cpl);

            if (err == COA_OK)
            {
                // check the current program
                if (cpl->default_program_id == pid)
                {
                    cpl->default_program_id = 0;
                    cpl->current_program = NULL;
                }
                else if (cpl->default_program_id > pid)
                {
                    cpl->default_program_id--;
                }
                else
                {
                    // do nothing
                }
            }

            return err;
        }
        cp_prev = cp;
        cp = cp->next;
    }

    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : set_current_step
 - Description    : set the current coating program step
 - Input          : [coating_program_list_t *cpl, int pid, int step_id]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 -                  - step_id: the coating program step id
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                  - COA_ERR_STEP_ID_NOT_FOUND: the step id is not found
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t set_current_step(coating_program_list_t *cpl, int pid, int step_id)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }
    // find the coating program
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        if (cp->id == pid)
        {
            // find the step
            step_t *step = cp->steps;
            cp->current_step = NULL;
            while (step)
            {
                if (step->id == step_id)
                {
                    cp->current_step = step;
                    return COA_OK;
                }
                step = step->next;
            }
            return COA_ERR_STEP_ID_NOT_FOUND;
        }
        cp = cp->next;
    }

    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : set_current_program
 - Description    : set the current coating program
 - Input          : [coating_program_list_t *cpl, int pid]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id of the current coating program
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t set_current_program(coating_program_list_t *cpl, int pid)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        if (cp->id == pid)
        {
            cpl->current_program = cp;
            cpl->default_program_id = pid;
            return COA_OK;
        }
        cp = cp->next;
    }

    cpl->default_program_id = 0;
    cpl->current_program = NULL;

    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : set_step_status
 - Description    : set the coating program step status
 - Input          : [coating_program_list_t *cpl, int pid, int step_id, bool status]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 -                  - step_id: the coating program step id
 -                  - status: the coating program step status
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                  - COA_ERR_STEP_ID_NOT_FOUND: the step id is not found
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t set_step_status(coating_program_list_t *cpl, int pid, int step_id, bool status)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        if (cp->id == pid)
        {
            // find the step
            step_t *step = cp->steps;
            while (step)
            {
                if (step->id == step_id)
                {
                    step->status = status;
                    return COA_OK;
                }
                step = step->next;
            }
            return COA_ERR_STEP_ID_NOT_FOUND;
        }
        cp = cp->next;
    }

    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : edit_step
 - Description    : edit the coating program step
 - Input          : [coating_program_list_t *cpl, int pid, int step_id, string notes, int duration, int cooldown]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 -                  - step_id: the coating program step id
 -                  - notes: the coating program step notes
 -                  - duration: the coating program step duration
 -                  - cooldown: the coating program step cooldown
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                  - COA_ERR_STEP_ID_NOT_FOUND: the step id is not found
 -                  - COA_ERR_NO_MEM: no memory
 - Note           : if the notes is null or empty, the notes will not be edited
                    if the duration is -1, the duration will not be edited
                    if the cooldown is -1, the cooldown will not be edited
 - Attention      : None
 *******************************************************************************/
coa_err_t edit_step(coating_program_list_t *cpl, int pid, int step_id, string notes, int duration, int cooldown, int power_set)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        if (cp->id == pid)
        {
            // find the step
            step_t *step = cp->steps;
            while (step)
            {
                if (step->id == step_id)
                {
                    // edit the step notes
                    if (notes != NULL && strlen(notes) > 0)
                    {
                        if (step->notes != NULL)
                        {
                            free(step->notes);
                        }
                        step->notes = malloc(strlen(notes) + 1);
                        if (step->notes == NULL)
                        {
                            return COA_ERR_NO_MEM;
                        }
                        strcpy(step->notes, notes);
                        step->notes[strlen(notes)] = '\0';
                    }

                    // edit the step duration
                    if (duration >= DEFAULT_STEP_DURATION_MIN && duration <= DEFAULT_STEP_DURATION_MAX)
                    {
                        step->duration = duration;
                    }
                    else
                    {
                        return COA_ERR_STEP_DURATION_NOT_IN_RANGE;
                    }

                    // edit the step cooldown
                    if (cooldown >= DEFAULT_STEP_COOLDOWN_MIN && cooldown <= DEFAULT_STEP_COOLDOWN_MAX)
                    {
                        step->cooldown = cooldown;
                    }
                    else
                    {
                        return COA_ERR_STEP_COOLDOWN_NOT_IN_RANGE;
                    }

                    // edit the step power
                    if (power_set >= DEFAULT_STEP_POWER_MIN && power_set <= DEFAULT_STEP_POWER_MAX)
                    {
                        step->power_set = power_set;
                    }
                    else
                    {
                        return COA_ERR_STEP_POWER_NOT_IN_RANGE;
                    }
                    return COA_OK;
                }
                step = step->next;
            }
            return COA_ERR_STEP_ID_NOT_FOUND;
        }
        cp = cp->next;
    }

    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : edit_program
 - Description    : edit the coating program
 - Input          : [coating_program_list_t *cpl, int pid, string name, string description]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 -                  - name: the coating program name
 -                  - description: the coating program description
 - Output         : None
 - Return         : coa_err_t
 -                  - COA_OK: success
 -                  - COA_ERR: failed
 -                  - COA_ERR_CPL_IS_NULL: the cpl is null
 -                  - COA_ERR_PID_NOT_FOUND: the pid is not found
 -                  - COA_ERR_NO_MEM: no memory
 - Note           : the name and description will be edited if the name and description is not null or empty
 - Attention      : this function will edit the coating program name and description
                   if the cp is null, this function will return COA_ERR_CP_IS_NULL
                   if the pid is not found, this function will return COA_ERR_PID_NOT_FOUND
 *******************************************************************************/
coa_err_t edit_program(coating_program_list_t *cpl, int pid, string name, string description)
{
    // check the coating program
    if (cpl == NULL)
    {
        return COA_ERR_CP_IS_NULL;
    }

    // find the coating program
    coating_program_t *cp_p = cpl->program;
    while (cp_p)
    {
        if (cp_p->id == pid)
        {
            // edit the coating program name
            if (name != NULL && strlen(name) > 0)
            {
                if (cp_p->name != NULL)
                {
                    free(cp_p->name);
                }
                cp_p->name = malloc(strlen(name) + 1);
                if (cp_p->name == NULL)
                {
                    return COA_ERR_NO_MEM;
                }
                strcpy(cp_p->name, name);
                cp_p->name[strlen(name)] = '\0';
            }

            // edit the coating program description
            if (description != NULL && strlen(description) > 0)
            {
                if (cp_p->description != NULL)
                {
                    free(cp_p->description);
                }
                cp_p->description = malloc(strlen(description) + 1);
                if (cp_p->description == NULL)
                {
                    return COA_ERR_NO_MEM;
                }
                strcpy(cp_p->description, description);
                cp_p->description[strlen(description)] = '\0';
            }

            return COA_OK;
        }
        cp_p = cp_p->next;
    }

    return COA_ERR_PID_NOT_FOUND;
}

/*******************************************************************************
 - Function Name  : new_step_to_program_at_tail
 - Description    : new a step to the coating program at tail
 - Input          : [coating_program_list_t *cpl, int program_id]
 -                  - cpl: the coating program list pointer
 -                  - program_id: the coating program id
 - Output         : None
 - Return         : COA_OK: success
 -                  COA_ERR: failed
 -                  COA_ERR_CP_IS_NULL: the cp is null
 -                  COA_ERR_NO_MEM: no memory
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coa_err_t new_step_to_program_at_tail(coating_program_list_t *cpl, int program_id)
{
    // check the coating program
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    coating_program_t *cp = get_program_by_id(cpl, program_id);
    if (cp == NULL)
    {
        return COA_ERR_CP_IS_NULL;
    }

    if (cp->steps == NULL)
    {
        cp->steps = new_step(1, DEFAULT_STEP_STATUS, DEFAULT_STEP_NOTES, DEFAULT_STEP_DURATION, DEFAULT_STEP_COOLDOWN, DEFAULT_STEP_POWER);
        if (cp->steps == NULL)
        {
            return COA_ERR_NO_MEM;
        }
        cp->steps_count = 1;
        return COA_OK;
    }

    // find the last step
    step_t *step = cp->steps;
    while (step->next != NULL)
    {
        step = step->next;
    }

    int step_cnt = get_steps_count(cpl, program_id);
    // new a step
    step->next = new_step(step_cnt + 1, DEFAULT_STEP_STATUS, DEFAULT_STEP_NOTES, DEFAULT_STEP_DURATION, DEFAULT_STEP_COOLDOWN, DEFAULT_STEP_POWER);
    if (step->next == NULL)
    {
        return COA_ERR_NO_MEM;
    }

    cp->steps_count = step_cnt + 1;

    return COA_OK;
}

/*******************************************************************************
 * Function Name  : new_coating_program
 * Description    : new a coating program
 * Input          : [coating_program_list_t *cpl]
 *                 - cpl: the coating program list pointer
 * Output         : None
 * Return         : coa_err_t
 *                 - COA_OK: success
 *                 - COA_ERR: failed
 *                 - COA_ERR_CPL_IS_NULL: the coating program list is null
 *                 - COA_ERR_NO_MEM: no memory
 * Note           : None
 * Example        : new_program(&cpl);
 *******************************************************************************/
coa_err_t new_coating_program(coating_program_list_t *cpl)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // find last coating program
    coating_program_t *cp_new = NULL;

    // new a coating program
    cp_new = malloc(sizeof(coating_program_t));
    if (cp_new == NULL)
    {
        return COA_ERR_NO_MEM;
    }

    // set all of the coating program value to 0 and null
    memset(cp_new, 0, sizeof(coating_program_t));

    // set the coating program id
    cp_new->id = cpl->program_count + 1;

    // set the coating program name
    cp_new->name = malloc(strlen(DEFAULT_COPRO_NAME) + 1);
    if (cp_new->name == NULL)
        goto END;

    strcpy(cp_new->name, DEFAULT_COPRO_NAME);

    // set the coating program description
    cp_new->description = malloc(strlen(DEFAULT_COPRO_DESCRIPTION) + 1);
    if (cp_new->description == NULL)
        goto END;

    strcpy(cp_new->description, DEFAULT_COPRO_DESCRIPTION);

    // set the coating program steps count
    cp_new->steps_count = DEFAULT_COPRO_STEPS_COUNT;

    if (cp_new->steps_count > 0)
    {
        // set the first step
        if (cp_new->steps == NULL)
        {
            cp_new->steps = new_step(1, DEFAULT_STEP_STATUS, DEFAULT_STEP_NOTES, DEFAULT_STEP_DURATION, DEFAULT_STEP_COOLDOWN, DEFAULT_STEP_POWER);
        }

        step_t *step = cp_new->steps;
        for (int i = 1; i < cp_new->steps_count; i++)
        {
            step->next = new_step(i + 1, DEFAULT_STEP_STATUS, DEFAULT_STEP_NOTES, DEFAULT_STEP_DURATION, DEFAULT_STEP_COOLDOWN, DEFAULT_STEP_POWER);
            if (step->next == NULL)
                goto END;
            step = step->next;
        }
    }

    // insert the coating program
    if (cpl->program == NULL)
    {
        cpl->program = cp_new;
    }
    else
    {
        coating_program_t *cp = cpl->program;
        while (cp->next != NULL)
        {
            cp = cp->next;
        }
        cp->next = cp_new;
    }

    // set the coating program count
    cpl->program_count = get_coating_program_count(cpl);

    return COA_OK;

END:
    free_program_mem(&cp_new);
    return COA_ERR_NO_MEM;
}

/*******************************************************************************
 * Function Name  : save_coating_program_list_no_space
 * Description    : save the coating program list to json file without space
 * Input          : [coating_program_list_t *cpl, string json_file_path, bool cover]
 *                 - cpl: the coating program list pointer
 *                 - json_file_path: the json file path
 * Output         : None
 * Return         : coa_err_t
 *                 - COA_OK: success
 *                 - COA_ERR: failed
 *                 - COA_ERR_JSON_FILE_EXIST: the json file exist
 *                 - COA_ERR_JSON_FILE_WRITE_FAILED: the json file write failed
 * Note           : None
 * Example        : save_coating_program_list_no_space(&cpl, "coating_program.json", true);
 *******************************************************************************/
coa_err_t save_coating_program_list_no_space(coating_program_list_t *cpl, string json_file_path, bool cover)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // check the json file path
    if (json_file_path == NULL || strlen(json_file_path) == 0)
    {
        return COA_ERR_JSON_FILE_PATH_IS_INVALID;
    }

    if (!cover)
    {
        // check the json file if exist
        FILE *fp = fopen(json_file_path, "rb");
        if (fp != NULL)
        {
            fclose(fp);
            return COA_ERR_JSON_FILE_EXIST;
        }
    }

    // open the json file
    FILE *fp = fopen(json_file_path, "wb");
    if (fp == NULL)
    {
        return COA_ERR_JSON_FILE_WRITE_FAILED;
    }

    // write the json file
    fprintf(fp, "{");
    fprintf(fp, "\"program-count\":%d,", cpl->program_count);
    fprintf(fp, "\"default-program\":%d,", cpl->default_program_id);
    fprintf(fp, "\"program-list\":[");
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        fprintf(fp, "{");
        fprintf(fp, "\"id\":%d,", cp->id);
        fprintf(fp, "\"name\":\"%s\",", cp->name);
        fprintf(fp, "\"description\":\"%s\",", cp->description);
        fprintf(fp, "\"steps_count\":%d,", cp->steps_count);
        fprintf(fp, "\"steps\":[");
        step_t *step = cp->steps;
        while (step)
        {
            fprintf(fp, "{");
            fprintf(fp, "\"id\":%d,", step->id);
            fprintf(fp, "\"status\":%s,", step->status ? "true" : "false");
            fprintf(fp, "\"duration\":%d,", step->duration);
            fprintf(fp, "\"cooldown\":%d,", step->cooldown);
            fprintf(fp, "\"power\":%d,", step->power_set);
            // printf("step->power_set:%d\n", step->power_set);
            fprintf(fp, "\"notes\":\"%s\"", step->notes);
            fprintf(fp, "}");
            step = step->next;
            if (step)
            {
                fprintf(fp, ",");
            }
        }
        fprintf(fp, "]");
        fprintf(fp, "}");
        cp = cp->next;
        if (cp)
        {
            fprintf(fp, ",");
        }
    }
    fprintf(fp, "]");
    fprintf(fp, "}");

    // close the json
    fclose(fp);

    return COA_OK;
}

/*******************************************************************************
 * Function Name  : save_coating_program_list
 * Description    : save the coating program list to json file
 * Input          : [coating_program_list_t *cpl, string json_file_path, bool cover]
 *                  - cpl: the coating program list pointer
 *                  - json_file_path: the json file path
 * Output         : None
 * Return         : coa_err_t
 *                 - COA_OK: success
 *                 - COA_ERR: failed
 *                 - COA_ERR_JSON_FILE_EXIST: the json file exist
 *                 - COA_ERR_JSON_FILE_WRITE_FAILED: the json file write failed
 * Note           : None
 * Example        : save_coating_program_list(&cpl, "coating_program.json", true);
 *******************************************************************************/
coa_err_t save_coating_program_list(coating_program_list_t *cpl, string json_file_path, bool cover)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }

    // check the json file path
    if (json_file_path == NULL || strlen(json_file_path) == 0)
    {
        return COA_ERR_JSON_FILE_PATH_IS_INVALID;
    }

    if (!cover)
    {
        // check the json file if exist
        FILE *fp = fopen(json_file_path, "rb");
        if (fp != NULL)
        {
            fclose(fp);
            return COA_ERR_JSON_FILE_EXIST;
        }
    }

    // open the json file
    FILE *fp = fopen(json_file_path, "wb");
    if (fp == NULL)
    {
        return COA_ERR_JSON_FILE_WRITE_FAILED;
    }

    // write the json file
    fprintf(fp, "{\n");
    fprintf(fp, "  \"program-count\": %d,\n", cpl->program_count);
    fprintf(fp, "  \"default-program\": %d,\n", cpl->default_program_id);
    fprintf(fp, "  \"program-list\": [\n");
    coating_program_t *cp = cpl->program;
    while (cp)
    {
        fprintf(fp, "    {\n");
        fprintf(fp, "      \"id\": %d,\n", cp->id);
        fprintf(fp, "      \"name\": \"%s\",\n", cp->name);
        fprintf(fp, "      \"description\": \"%s\",\n", cp->description);
        fprintf(fp, "      \"steps_count\": %d,\n", cp->steps_count);
        fprintf(fp, "      \"steps\": [\n");
        step_t *step = cp->steps;
        while (step)
        {
            fprintf(fp, "        {\n");
            fprintf(fp, "          \"id\": %d,\n", step->id);
            fprintf(fp, "          \"status\": %s,\n", step->status ? "true" : "false");
            fprintf(fp, "          \"duration\": %d,\n", step->duration);
            fprintf(fp, "          \"cooldown\": %d,\n", step->cooldown);
            fprintf(fp, "          \"power\": %d,\n", step->power_set);
            fprintf(fp, "          \"notes\": \"%s\"\n", step->notes);
            fprintf(fp, "        }");
            step = step->next;
            if (step)
            {
                fprintf(fp, ",");
            }
            fprintf(fp, "\n");
        }
        fprintf(fp, "      ]\n");
        fprintf(fp, "    }");
        cp = cp->next;
        if (cp)
        {
            fprintf(fp, ",");
        }
        fprintf(fp, "\n");
    }
    fprintf(fp, "  ]\n");
    fprintf(fp, "}\n");

    // close the json file
    fclose(fp);

    return COA_OK;
}

/*******************************************************************************
 * Function Name  : print_coating_program_list
 * Description    : print the coating program list
 * Input          : [coating_program_list_t *cpl, bool print_steps, bool print_current_program]
 *                  - cpl: the coating program list pointer
 *                  - print_steps: print the coating program steps
 *                  - print_current_program: print the current coating program
 * Output         : None
 * Return         : None
 * Note           : None
 * Example        : print_coating_program_list(&cpl, true, true);
 *******************************************************************************/
void print_coating_program_list(coating_program_list_t *cpl, bool print_steps, bool print_current_program)
{
    // check the coating program list
    if (cpl == NULL)
    {
        printf("the coating program list is null\n");
        return;
    }
    printf("Coating program list:\n");
    printf("{\n");
    printf("  program_count: %d,\n", cpl->program_count);
    printf("  default_program_id: %d,\n", cpl->default_program_id);
    printf("  program-list: [\n");
    coating_program_t *cp = cpl->program;
    for (int i = 0; i < cpl->program_count; i++)
    {
        printf("    {\n");
        printf("      id: %d,\n", cp->id);
        printf("      name: %s,\n", cp->name);
        printf("      description: %s,\n", cp->description);
        printf("      steps_count: %d,\n", cp->steps_count);
        if (print_steps)
        {
            printf("      steps: [\n");
            step_t *step = cp->steps;
            while (step)
            {
                printf("        {\n");
                printf("          id: %d,\n", step->id);
                printf("          status: %s,\n", step->status ? "true" : "false");
                printf("          duration: %d,\n", step->duration);
                printf("          cooldown: %d,\n", step->cooldown);
                printf("          power: %d,\n", step->power_set);
                printf("          notes: %s\n", step->notes);
                printf("        }");
                step = step->next;
                if (step)
                {
                    printf(",");
                }
                printf("\n");
            }
            printf("      ]\n");
        }
        printf("    }");
        cp = cp->next;
        if (cp)
        {
            printf(",");
        }
        printf("\n");
    }
    printf("  ]\n");
    printf("}\n");

    if (print_current_program && cpl->current_program != NULL)
    {
        printf("Current program:\n");
        printf("{\n");
        printf("  id: %d,\n", cpl->current_program->id);
        printf("  name: %s,\n", cpl->current_program->name);
        printf("  description: %s,\n", cpl->current_program->description);
        printf("  steps_count: %d,\n", cpl->current_program->steps_count);
        if (print_steps)
        {
            printf("  steps: [\n");
            step_t *step = cpl->current_program->steps;
            while (step)
            {
                printf("    {\n");
                printf("      id: %d,\n", step->id);
                printf("      status: %s,\n", step->status ? "true" : "false");
                printf("      notes: %s,\n", step->notes);
                printf("      duration: %d,\n", step->duration);
                printf("      cooldown: %d\n", step->cooldown);
                printf("    }");
                step = step->next;
                if (step)
                {
                    printf(",");
                }
                printf("\n");
            }
            printf("  ]\n");
        }
        printf("}\n");
    }
}

/*******************************************************************************
 * Function Name  : coating_program_list_init
 * Description    : init the coating program list
 * Input          : [coating_program_list_t *cpl, string json_file_path]
 *                  - cpl: the coating program list pointer
 *                  - json_file_path: the json file path
 * Output         : None
 * Return         : COA_OK: success
 *                  COA_ERR: failed
 *                  COA_ERR_JSON_FILE_NOT_EXIST: the json file not exist
 * Note           : None
 *******************************************************************************/
coa_err_t coating_program_list_init(coating_program_list_t *cpl, string json_file_path)
{
    coa_err_t err = COA_OK;
    // check the coating program list
    if (cpl == NULL)
    {
        return COA_ERR_CPL_IS_NULL;
    }
    memset(cpl, 0, sizeof(coating_program_list_t));

    // check the json file path
    if (json_file_path == NULL || strlen(json_file_path) == 0)
    {
        return COA_ERR_JSON_FILE_NOT_EXIST;
    }

    // set the json file path
    cpl->json_file_path = malloc(strlen(json_file_path) + 1);
    if (cpl->json_file_path == NULL)
    {
        return COA_ERR;
    }

    strcpy(cpl->json_file_path, json_file_path);

    // check the json file if exist
    FILE *fp = fopen(json_file_path, "rb");
    if (fp == NULL)
    {
        return COA_ERR_JSON_FILE_NOT_EXIST;
    }
    fclose(fp);

    // read the file to buffer
    js_vessel_t js_vessel;
    json_err_t jserr = read_json_file(json_file_path, (byte **)(&js_vessel.json), true);
    if (js_vessel.json == NULL || jserr != JSON_OK)
    {
        err = COA_ERR_JSON_FILE_READ_FAILED;
        goto free_mem;
    }

    jserr = parse_json_buffer(&js_vessel);
    if (jserr != JSON_OK)
    {
        err = COA_ERR_JSON_FILE_PARSE_ERROR;
        goto free_mem;
    }

    // get the coating program count
    get_number_by_path(&js_vessel, "program-count", &cpl->program_count, 0);
    if (cpl->program_count == 0)
    {
        err = COA_ERR_CP_CNT_NOT_FOUND;
        goto free_mem;
    }

    // get the default coating program id, if the default program id is not found, set the default program id to 1
    get_number_by_path(&js_vessel, "default-program", &cpl->default_program_id, 1);

    // malloc the coating_program_t memory
    coating_program_t **cp = &cpl->program;
    for (int i = 0; i < cpl->program_count; i++)
    {
        *cp = malloc(sizeof(coating_program_t));
        if (*cp == NULL)
        {
            err = COA_ERR_NO_MEM;
            goto free_mem;
        }
        memset(*cp, 0, sizeof(coating_program_t));
        cp = &(*cp)->next;
    }

    // check the coating program count and the coating program list count
    int cp_array_size = 0;
    jserr = get_array_size_by_path(&js_vessel, "program-list", &cp_array_size);
    if (jserr != JSON_OK)
    {
        err = COA_ERR_CP_LIST_NOT_FOUND;
        goto free_mem;
    }

    if (cp_array_size != cpl->program_count)
    {
        // if the coating program count is not match the coating program list count, use the max one
        cpl->program_count = pf_max(cp_array_size, cpl->program_count);
    }

    // check the default program id
    if (cpl->default_program_id > cpl->program_count)
    {
        // if the default program id is not in range, set the default program id to 1
        cpl->default_program_id = 1;
    }

    // load coating program
    err = coating_programs_load(cpl->program, cpl->program_count, &js_vessel);
    if (err == COA_OK)
    {
        // set the current program
        cpl->current_program = cpl->program;
        for (int i = 0; i < cpl->default_program_id - 1; i++)
        {
            cpl->current_program = cpl->current_program->next;
        }
        free_json_vessel(&js_vessel);
        return COA_OK;
    }

// some error return with free memory
free_mem:
    // free(cpl->json_file_path);    // free the json file path memory
    free_json_vessel(&js_vessel); // free the json vessel memory
    free_program_list(cpl);       // free the coating program memory

    return err;
}

/*******************************************************************************
 * Function Name  : coating_programs_load
 * Description    : load the coating process from json
 * Input          : [coating_program_t *cp, int cp_cnt, js_vessel_t *js_vessel]
 *                  - cp: the coating process pointer
 *                  - cp_cnt: the coating process count
 *                  - js_vessel: the json vessel
 * Output         : None
 * Return         : coa_err_t
 *                  - COA_OK: success
 *                  - COA_ERR: failed
 *                  - COA_ERR_JSON_VESSLE_ERROR: the json vessel error
 *                  - COA_ERR_CP_IS_NULL: the cp is null
 *                  - COA_ERR_PID_NOT_IN_RANGE: the cp id is not in range
 * Note           : None
 *******************************************************************************/
coa_err_t coating_programs_load(coating_program_t *cp, int cp_cnt, js_vessel_t *js_vessel)
{
    // check parameters
    json_err_t err = check_json_vessel(js_vessel);
    if (err != JSON_OK)
    {
        return COA_ERR_JSON_VESSLE_ERROR;
    }

    // check if the cp is null
    if (cp == NULL)
    {
        return COA_ERR_CP_IS_NULL;
    }

    char path[COATING_PRO_JSON_PATH_MAX_LEN];

    for (int cp_id = 0; cp_id < cp_cnt; cp_id++)
    {

        // get the coating process id
        sprintf(path, "program-list[%d].id", cp_id);
        get_number_by_path(js_vessel, path, &cp->id, DEFAULT_COPRO_ID);

        // get the coating process name
        sprintf(path, "program-list[%d].name", cp_id);
        get_string_by_path_alloc(js_vessel, path, &cp->name, DEFAULT_COPRO_NAME, MAX_PRO_NAME_LEN - 1);

        // get the coating process description
        sprintf(path, "program-list[%d].description", cp_id);
        get_string_by_path_alloc(js_vessel, path, &cp->description, DEFAULT_COPRO_DESCRIPTION, MAX_PRO_DESCRIPTION_LEN - 1);

        // get the coating process steps count
        sprintf(path, "program-list[%d].steps_count", cp_id);
        get_number_by_path(js_vessel, path, &cp->steps_count, DEFAULT_COPRO_STEPS_COUNT);

        step_t **step = &cp->steps;
        int steps_count;

        // get the coating process steps
        sprintf(path, "program-list[%d].steps", cp_id);
        get_array_size_by_path(js_vessel, path, &steps_count);

        // check the coating process steps count
        if (steps_count != cp->steps_count)
        {
            // return COA_ERR_STEPS_COUNT_NOT_MATCH;
            // printf("steps count not match\n");
            // printf("use array size: %d", steps_count);
            cp->steps_count = steps_count;
        }

        // get the coating process steps
        for (int i = 0; i < cp->steps_count; i++)
        {
            // allocate the coating process step value memory
            int step_id = 0;
            bool step_status = false;
            char step_notes[MAX_STEP_NOTES_LEN];
            int step_duration = DEFAULT_STEP_DURATION;
            int step_cooldown = DEFAULT_STEP_COOLDOWN;
            int step_power = DEFAULT_STEP_POWER;

            // get the coating process step's id path
            sprintf(path, "program-list[%d].steps[%d].id", cp_id, i);
            get_number_by_path(js_vessel, path, &step_id, i + 1);

            // get the coating process step's status
            sprintf(path, "program-list[%d].steps[%d].status", cp_id, i);
            get_bool_by_path(js_vessel, path, &step_status, DEFAULT_STEP_STATUS);

            // get the coating process step's notes
            sprintf(path, "program-list[%d].steps[%d].notes", cp_id, i);
            get_string_by_path(js_vessel, path, step_notes, DEFAULT_STEP_NOTES, MAX_STEP_NOTES_LEN - 1);

            // get the coating process step's duration
            sprintf(path, "program-list[%d].steps[%d].duration", cp_id, i);
            get_number_by_path(js_vessel, path, &step_duration, DEFAULT_STEP_DURATION);

            // get the coating process step's cooldown
            sprintf(path, "program-list[%d].steps[%d].cooldown", cp_id, i);
            get_number_by_path(js_vessel, path, &step_cooldown, DEFAULT_STEP_COOLDOWN);

            // get the coating process step's power
            sprintf(path, "program-list[%d].steps[%d].power", cp_id, i);
            get_number_by_path(js_vessel, path, &step_power, DEFAULT_STEP_POWER);

            *step = new_step(step_id, step_status, step_notes, step_duration, step_cooldown, step_power);
            step = &(*step)->next;
        }
        // move to the next coating process
        cp = cp->next;
    }
    return COA_OK;
}

/*******************************************************************************
 * Function Name  : get_coating_program_count
 * Description    : get the coating program count
 * Input          : [coating_program_list_t *cpl]
 *                  - cpl: the coating program list pointer
 * Output         : None
 * Return         : int: the coating program count
 * Note           : None
 *******************************************************************************/
int get_coating_program_count(coating_program_list_t *cpl)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return 0;
    }

    // check the coating program count
    coating_program_t *cp_p = cpl->program;
    int count = 0;
    while (cp_p != NULL)
    {
        count++;
        cp_p = cp_p->next;
    }

    cpl->program_count = count;

    return cpl->program_count;
}

/*******************************************************************************
 - Function Name  : get_steps_count
 - Description    : get the steps count
 - Input          : [coating_program_t *cp, int pid]
 -                  - cp: the coating program pointer
 -                  - pid: the coating program id
 - Output         : None
 - Return         : int: the steps count
 -                  -1: failed
 - Note           : None
 - Attention      : None
 *******************************************************************************/
int get_steps_count(coating_program_list_t *cpl, int pid)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return -1;
    }

    // check the coating program id
    cpl->program_count = get_coating_program_count(cpl);

    if (pid <= 0 || pid > cpl->program_count)
    {
        return -1;
    }

    // get the coating program
    coating_program_t *cp = get_program_by_id(cpl, pid);

    // check the steps count
    step_t *step = cp->steps;
    int count = 0;
    while (step != NULL)
    {
        count++;
        step = step->next;
    }

    cp->steps_count = count;

    return cp->steps_count;
}

/*******************************************************************************
 - Function Name  : get_program_by_id
 - Description    : get the coating program by id
 - Input          : [coating_program_list_t *cpl, int pid]
 -                  - cpl: the coating program list pointer
 -                  - pid: the coating program id
 - Output         : None
 - Return         : coating_program_t *
 -                  - the coating program pointer, if failed, return NULL
 - Note           : None
 - Attention      : None
 *******************************************************************************/
coating_program_t *get_program_by_id(coating_program_list_t *cpl, int pid)
{
    // check the coating program list
    if (cpl == NULL)
    {
        return NULL;
    }

    // check the coating program id
    cpl->program_count = get_coating_program_count(cpl);
    if (pid <= 0 || pid > cpl->program_count)
    {
        return NULL;
    }

    // get the coating program
    coating_program_t *cp = cpl->program;
    while (cp != NULL)
    {
        if (cp->id == pid)
        {
            return cp;
        }
        cp = cp->next;
    }

    return NULL;
}

/*******************************************************************************
 - Function Name: new_step
 - Description  : create a new step
 - Input        : [int id, bool status, string notes, int duration, int cooldown, int power]
 -               - id: step id
 -               - status: step status
 -               - notes: step notes
 -               - duration: step duration
 -               - cooldown: step cooldown
 -               - power: step power
 - Return Value : step_t *
 -               - the new step, if failed, return NULL
 - Note         : none
 - Attention    : none
 - Example      : step_t *step = new_step(1, true, "step 1", 10, 5);
 *******************************************************************************/
step_t *new_step(int id, bool status, string notes, int duration, int cooldown, int power)
{
    step_t *step = (step_t *)malloc(sizeof(step_t));
    step->id = id;
    step->status = status;
    step->notes = (string)malloc(strlen(notes) + 1);
    strcpy(step->notes, notes);
    step->duration = (duration >= DEFAULT_STEP_DURATION_MIN && duration <= DEFAULT_STEP_DURATION_MAX) ? duration : DEFAULT_STEP_DURATION;
    step->cooldown = (cooldown >= DEFAULT_STEP_COOLDOWN_MIN && cooldown <= DEFAULT_STEP_COOLDOWN_MAX) ? cooldown : DEFAULT_STEP_COOLDOWN;
    step->power_set = (power >= DEFAULT_STEP_POWER_MIN && power <= DEFAULT_STEP_POWER_MAX) ? power : DEFAULT_STEP_POWER;
    step->next = NULL;
    return step;
}

/*******************************************************************************
 - Function Name: free_program_list
 - Description  : free process list memory
 - Input        : [coating_program_list_t *cpl]
 -               - cpl: the process list to be freed
 - Return Value : none
 - Note         : none
 - Attention    : none
 *******************************************************************************/
void free_program_list(coating_program_list_t *cpl)
{
    if (cpl == NULL)
        return;
    coating_program_t *cp_p = cpl->program;
    while (cp_p)
    {
        free_program_mem(&cp_p);
        cp_p = cp_p->next;
    }
    cpl->current_program = NULL;
    cpl->program = NULL;
    cpl->default_program_id = 0;
    cpl->program_count = 0;
    // if (cpl->json_file_path)
    // free(cpl->json_file_path);
    // cpl->json_file_path = NULL;
    // free(cpl);
}

/*******************************************************************************
 - Function Name: free_program_mem
 - Description  : free process memory
 - Input        : [coating_program_t **cp]
 -               - cp: the process to be freed
 - Return Value : none
 - Note         : none
 - Attention    : none
 *******************************************************************************/
void free_program_mem(coating_program_t **cp)
{

    coating_program_t *cp_p = *cp;
    free_steps_mem(&cp_p->steps); // free steps memory

    // free process name and description
    if (cp_p->name)
        free(cp_p->name);

    if (cp_p->description)
        free(cp_p->description);

    free(cp_p);
    *cp = NULL;
}

/*******************************************************************************
 - Function Name: free_step_mem
 - Description  : free step memory
 - Input        : [step_t **step]
 -               - step: the step to be freed
 - Return Value : none
 - Note         : none
 - Attention    : none
 *******************************************************************************/
void free_step_mem(step_t **step)
{
    if (*step)
    {
        if ((*step)->notes)
            free((*step)->notes);
        free(*step);
        *step = NULL;
    }
}

/*******************************************************************************
 - Function Name: free_steps_mem
 - Description  : free steps memory
 - Input        : [step_t **steps]
 -               - steps: the steps to be freed
 - Return Value : none
 - Note         : none
 - Attention    : none
 *******************************************************************************/
void free_steps_mem(step_t **steps)
{
    step_t *step = *steps;
    while (step)
    {
        step_t *step_next = step->next;
        free_step_mem(&step);
        step = step_next;
    }
    *steps = NULL;
}
